package uk.ac.imperial.gpaexpress.server;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

@SuppressWarnings("serial")
public class Login extends HttpServlet {
	Connection conn;
	String type;
	String email;
	String password_given;
	String password_valid;
	
	String db_url = "jdbc:postgresql:richard";
	String db_user = "richard";
	String db_password = "rhymenoceros";
	
	@Override  
	public void doPost(HttpServletRequest req, HttpServletResponse resp)
	throws ServletException, IOException {
		try {
			resp.setContentType("text/html");
			type = req.getHeader("type");
			email = req.getHeader("email");
			password_given = req.getHeader("password");
			password_valid = null;

			Class.forName( "org.postgresql.Driver" );

			conn = DriverManager.getConnection(
					db_url,db_user,db_password);

			if(type.equals("register")){
				Statement stmt = conn.createStatement();
				ResultSet rs = stmt.executeQuery("SELECT username FROM users WHERE username = '" + email + "'");
				if(rs.next()){
					resp.getWriter().write("email_taken");
				}else{
					PreparedStatement updateUsers = conn.prepareStatement("INSERT INTO users (username, password) VALUES (?,?)");
					updateUsers.setString(1, email);
					updateUsers.setString(2, getHash(password_given));
					updateUsers.execute();
					HttpSession session = req.getSession(true);
					session.setAttribute("email", email);
					resp.getWriter().write(createUserXML(email));
				}
			}else if(type.equals("login")){
				Statement stmt = conn.createStatement();
				ResultSet rs = stmt.executeQuery("SELECT password FROM users WHERE username = '" + email + "'");
				//if true then user email exists
				if(rs.next()){
					System.out.println("User exists.");
					password_valid = rs.getString("password");
					//if true then user password is valid
					if(getHash(password_given).equals(password_valid)){
						HttpSession session = req.getSession(true);
						session.setAttribute("email", email);
						try{
							resp.getWriter().write(createUserXML(email));
						} catch (Exception e) {
							System.err.println("Exception: " + e + "\n" + e.getMessage() );
						}
					}else{
						resp.getWriter().write("password_fail");
					}
				}else{
					resp.getWriter().write("email_fail");
				}
			}
			conn.close();
		} catch (Exception e) {
			System.err.println("Exception: " + e + "\n" + e.getMessage() );
		}

	}

	/* method takes password string and returns the corresponding SHA1 hash string representation */
	private String getHash(String password) throws NoSuchAlgorithmException, IOException{
		byte[] input = password.getBytes();
		MessageDigest hash = MessageDigest.getInstance("SHA1");

		ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(input);
		DigestInputStream digestInputStream = new DigestInputStream(byteArrayInputStream, hash);
		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

		int ch;
		while ((ch = digestInputStream.read()) >= 0) {
			byteArrayOutputStream.write(ch);
		}

		byte[] newInput = byteArrayOutputStream.toByteArray();

		byteArrayOutputStream = new ByteArrayOutputStream();
		DigestOutputStream digestOutputStream = new DigestOutputStream(byteArrayOutputStream, hash);
		digestOutputStream.write(newInput);
		digestOutputStream.close();

		return new String(digestOutputStream.getMessageDigest().digest());
	}

	/* method looks up username in database and returns xml detailing what models/solutions are available */
	private String createUserXML(String email) throws ParserConfigurationException, TransformerFactoryConfigurationError, TransformerException{
		DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
		Document document = docBuilder.newDocument();
		Element rootElement = document.createElement("info");
		document.appendChild(rootElement);
		Element userChild = document.createElement("user");
		userChild.setAttribute("email", email);
		rootElement.appendChild(userChild);

		try {
			conn = DriverManager.getConnection(
					db_url,db_user,db_password);
			Statement models_stmt = conn.createStatement();
			ResultSet models = models_stmt.executeQuery("SELECT id, title FROM models WHERE owner = '" + email + "'");
			// For each model title that user owns
			while(models.next()){
				String model_title = models.getString("title");
				String model_id = String.valueOf(models.getInt("id"));
				Element modelChild = document.createElement("model");
				modelChild.setAttribute("title", model_title);
				modelChild.setAttribute("id", model_id);

				Statement params_stmt = conn.createStatement();
				ResultSet params = params_stmt.executeQuery("SELECT params.title FROM models, params "
						+ " WHERE models.id=params.model_id AND owner = '" + email + "' AND models.title = '" + model_title + "'");
				// For each param set that belongs to model
				while(params.next()){
					String params_title = params.getString("title");
					Element paramsChild = document.createElement("params");
					paramsChild.setAttribute("title", params_title);
					modelChild.appendChild(paramsChild);
				}
				params_stmt.close();
				rootElement.appendChild(modelChild);
			}
			models_stmt.close();
			
			Statement notificationStmt = conn.createStatement();
			ResultSet notifications = notificationStmt.executeQuery("SELECT id, timestamp, message "
					+ "FROM notifications WHERE recipient = '" + email + "'");
			while(notifications.next()){
				int id = notifications.getInt("id");
				String timestamp = notifications.getString("timestamp");
				String message = notifications.getString("message");
				Element notificationChild = document.createElement("notification");
				notificationChild.setAttribute("timestamp", timestamp);
				notificationChild.setAttribute("message", message);
				rootElement.appendChild(notificationChild);
				Statement delNotificationStmt = conn.createStatement();
				delNotificationStmt.execute("DELETE FROM notifications WHERE id = " + id);
			}
			notificationStmt.close();
		}catch (Exception e){
			System.err.println("Exception: " + e + "\n" + e.getMessage() );
		}

		TransformerFactory transfac = TransformerFactory.newInstance();
		Transformer trans = transfac.newTransformer();
		trans.setOutputProperty(OutputKeys.INDENT, "yes");

		//create string from xml tree
		StringWriter sw = new StringWriter();
		StreamResult result = new StreamResult(sw);
		DOMSource source = new DOMSource(document);
		trans.transform(source, result);
		return sw.toString();
	}
}
